package com.wtwd.device.aspect;


import com.wtwd.device.annotation.LightUpMedal;
import com.wtwd.device.constant.RespCode;
import com.wtwd.device.model.dto.SportRecord;
import com.wtwd.device.model.entity.*;
import com.wtwd.device.model.vo.RespCodeEntity;
import com.wtwd.device.model.vo.RespEntity;
import com.wtwd.device.model.vo.ResultVO;
import com.wtwd.device.service.*;
import com.wtwd.device.util.DateUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author mjy
 * @date 2021/09/24
 * @description
 */
@Component
@Aspect
public class MedalAspect {

    /**
     * 此处切面为后置切面
     * 1.当数据同步完毕后,检测是否有需要点亮的勋章需要点亮
     */

/*    运动类型（呼吸：1,骑车或室外骑车:2,室内骑车:3,跑步机或室内跑步:4, 跑步或室外跑步:5,游泳:6, 走路:7,举重:8,瑜咖:9,羽毛球:10,
    篮球:11,跳绳:12,自由锻炼:13,足球:14,爬山:15,乒乓球:16,保龄球:17,开放水域:18,跳舞19，哑铃：20，呼啦圈：21，楼梯运动：22，
    踏步机：23，铁人三项：24,仰卧起坐：25，滑雪：26，台球：27，椭圆机：28，越野跑：29，健美操：30，普拉提：31，踢毽子：
            32，旋转：33，走步机：34)*/

    @Autowired
    private MedalService medalService;

    @Autowired
    private StepService stepService;

    @Autowired
    private AppUserService appUserService;

    @Autowired
    private AppDeviceService deviceService;

    @Autowired
    private AppUserTotalService userTotalService;

    private static final Logger logger = LoggerFactory.getLogger(MedalAspect.class);

    @AfterReturning(value = "@annotation(com.wtwd.device.annotation.LightUpMedal)", returning = "returnValue")
    public void lightUpMedal(JoinPoint pjp, Object returnValue) {
        //确保数据上传成功，才会进行以下操作
        if (returnValue instanceof ResultVO) {
            if (!((ResultVO) returnValue).getCode().equals(ResultVO.success().getCode())) {
                return;
            } else if (returnValue instanceof RespEntity) {
                if (!(((RespEntity) returnValue).getCode() == RespCode.SUCCESS.getCode())) {
                    return;
                }
            } else if (returnValue instanceof RespCodeEntity) {
                if ((((RespCodeEntity) returnValue).getCode() == RespCode.SUCCESS.getCode())) {
                    return;
                }
            }
        }
        //取得方法上的注解，根据方法上的注解点亮
        logger.info("后置通知执行 lightUpMedal : JoinPoint = " + pjp);
        //确保执行成功才会执行该后置
        Long userId = appUserService.getCurrentUserId();
        if (userId == null) {
            return;
        }
        //获取方法上面的注解,根据注解上的值不同,点亮不同的勋章
        LightUpMedal lightUpMedal = getControllerAnnotation(pjp);
        int medalType = lightUpMedal.medalType();
        List<Medal> medals = medalService.getUserNotBoundMedal(userId, medalType);
        switch (medalType) {
            case 1:
                lightUpSteps(userId, medals, pjp);
                break;
            case 2:
                lightUpRuns(userId, medals, pjp);
                break;
            case 3:
                lightUpRiding(userId, medals, pjp);
                break;
        }
        logger.info("后置通知完毕");
    }


    public AppUserTotal initAppUserToTal(AppUserTotal appUserTotal, Long userId) {
        if (appUserTotal == null) {
            //统计收集该用户历史数据
            appUserTotal = new AppUserTotal();
            appUserTotal.setUserId(userId);
            appUserTotal.setStepTotal(0);
            appUserTotal.setRunTotal(0);
            appUserTotal.setRidingTotal(0);
            appUserTotal.setHeatTotal(0);
            userTotalService.insert(appUserTotal);
        }
        return appUserTotal;
    }

    /**
     * 点亮步数
     *
     * @param userId
     * @param medals
     */
    public void lightUpSteps(Long userId, List<Medal> medals, JoinPoint pjp) {
        logger.info("点亮步数 lightUpSteps : userId,medals " + userId, medals.toString(), pjp);
        Object[] args = pjp.getArgs();
        List<SportRecord> sportRecords = new ArrayList<>();
        for (int i = 0; i < args.length; i++) {
            sportRecords = (List<SportRecord>) args[0];
            if (sportRecords == null || sportRecords.size() == 0) {
                return;
            }
            sportRecords = sportRecords.stream().sorted(Comparator.comparing(SportRecord::getTime)).collect(Collectors.toList());
            break;
        }
        AppUserTotal userTotal = userTotalService.getAppUserTotalByUserId(userId);
        userTotal = initAppUserToTal(userTotal, userId);
        List<AppUserMedal> bathAddMedalList = new ArrayList<>();//此集合用于收集勋章的点亮
        if (medals != null || medals.size() > 0) {//无论是否有勋章需要点亮，都需统计数据
            for (Medal medal : medals) {
                if (medal.getTimeType().equals(1)) {//单日类型徽章
                    for (SportRecord sportRecord : sportRecords) {
                        if (sportRecord.getTotalSteps() >= medal.getUpToStandard()) {//总运动步数大于勋章目标值则记录
                            AppUserMedal appUserMedal = new AppUserMedal();
                            appUserMedal.setUserId(userId);
                            appUserMedal.setMedalId(medal.getId());
                            try {
                                appUserMedal.setObtainTime(DateUtils.parse(sportRecord.getTime(), "yyyy-MM-dd"));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                            bathAddMedalList.add(appUserMedal);
                            break;
                        }
                    }
                } else if (medal.getTimeType().equals(2)) {//累计类型徽章
                    //定义累加值
                    Integer addStepTotal = userTotal.getStepTotal();
                    for (SportRecord sportRecord : sportRecords) {
                        addStepTotal += sportRecord.getTotalSteps();
                        if (addStepTotal >= medal.getUpToStandard()) {
                            AppUserMedal appUserMedal = new AppUserMedal();
                            appUserMedal.setUserId(userId);
                            appUserMedal.setMedalId(medal.getId());
                            try {
                                appUserMedal.setObtainTime(DateUtils.parse(sportRecord.getTime(), "yyyy-MM-dd"));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                            bathAddMedalList.add(appUserMedal);
                            break;
                        }
                    }
                }
            }
        }
        //统计数据
        int sum = sportRecords.stream().mapToInt(SportRecord::getTotalSteps).sum();
        Integer stepTotal = userTotal.getStepTotal() + sum;//统计数据
        //上传统计之后的数据
        userTotalService.updateStepTotal(userId, stepTotal);
        //点亮勋章
        if (bathAddMedalList != null && bathAddMedalList.size() > 0) {
            medalService.bathAddUserMedal(bathAddMedalList);
        }
        //处理卡路里数据
        List<UpHeat> heats = new ArrayList<>();
        sportRecords.stream().forEach(sportRecord -> {
            UpHeat upHeat = new UpHeat();
            upHeat.setTotalCalories(sportRecord.getTotalCalories());
            upHeat.setTime(sportRecord.getTime());
            heats.add(upHeat);
        });
        lightUpHeat(userId,heats,userTotal);
    }

    /**
     * 点亮跑步勋章
     * @param userId
     * @param medals
     * @param pjp
     */
    public void lightUpRuns(Long userId, List<Medal> medals, JoinPoint pjp){
        logger.info("点亮跑步勋章 lightUpRuns : userId,medals " + userId, medals.toString(), pjp);
        Object[] args = pjp.getArgs();
        SportModeInfo sportModeInfo=null;
        for (int i = 0; i < args.length; i++) {
            sportModeInfo=(SportModeInfo) args[0];
        }
        if (sportModeInfo==null){
            return;
        }
        AppUserTotal userTotal = userTotalService.getAppUserTotalByUserId(userId);
        userTotal = initAppUserToTal(userTotal, userId);
        List<AppUserMedal> bathAddMedalList = new ArrayList<>();//此集合用于收集勋章的点亮
        Integer runTotal = userTotal.getRunTotal() +sportModeInfo.getDistance();//统计数据
        if (medals != null || medals.size() > 0) {//无论是否有勋章需要点亮，都需统计数据
            for (Medal medal : medals) {
                if (medal.getTimeType().equals(1)){//单日类型徽章
                    if (sportModeInfo.getDistance()>=medal.getUpToStandard()){
                        AppUserMedal appUserMedal = new AppUserMedal();
                        appUserMedal.setUserId(userId);
                        appUserMedal.setMedalId(medal.getId());
                        appUserMedal.setObtainTime(sportModeInfo.getTime());
                        bathAddMedalList.add(appUserMedal);
                    }
                }else if (medal.getTimeType().equals(2)){//某日类型徽章
                    if (runTotal>=medal.getUpToStandard()){
                        AppUserMedal appUserMedal = new AppUserMedal();
                        appUserMedal.setUserId(userId);
                        appUserMedal.setMedalId(medal.getId());
                        appUserMedal.setObtainTime(sportModeInfo.getTime());
                        bathAddMedalList.add(appUserMedal);
                    }
                }
            }
        }
        //上传统计之后的数据
        userTotalService.updateRunTotal(userId, runTotal);
        //点亮勋章
        if (bathAddMedalList != null && bathAddMedalList.size() > 0) {
            medalService.bathAddUserMedal(bathAddMedalList);
        }
        //处理卡路里数据
        List<UpHeat> heats = new ArrayList<>();
        UpHeat heat = new UpHeat();
        heat.setTime(DateUtils.format(sportModeInfo.getTime(),"yyyy-MM-dd"));
        heat.setTotalCalories(sportModeInfo.getCalories());
        heats.add(heat);
        lightUpHeat(userId,heats,userTotal);
    }

    //点亮骑行类徽章

    public void lightUpRiding(Long userId, List<Medal> medals, JoinPoint pjp){
        logger.info("点亮跑步勋章 lightUpRuns : userId,medals " + userId, medals.toString(), pjp);
        Object[] args = pjp.getArgs();
        SportModeInfo sportModeInfo=null;
        for (int i = 0; i < args.length; i++) {
            sportModeInfo=(SportModeInfo) args[0];
        }
        if (sportModeInfo==null){
            return;
        }
        AppUserTotal userTotal = userTotalService.getAppUserTotalByUserId(userId);
        userTotal = initAppUserToTal(userTotal, userId);
        List<AppUserMedal> bathAddMedalList = new ArrayList<>();//此集合用于收集勋章的点亮
        Integer ridingTotal = userTotal.getRidingTotal() +sportModeInfo.getDistance();//统计数据
        if (medals != null || medals.size() > 0) {//无论是否有勋章需要点亮，都需统计数据
            for (Medal medal : medals) {
                if (medal.getTimeType().equals(1)){//单日类型徽章
                    if (sportModeInfo.getDistance()>=medal.getUpToStandard()){
                        AppUserMedal appUserMedal = new AppUserMedal();
                        appUserMedal.setUserId(userId);
                        appUserMedal.setMedalId(medal.getId());
                        appUserMedal.setObtainTime(sportModeInfo.getTime());
                        bathAddMedalList.add(appUserMedal);
                    }
                }else if (medal.getTimeType().equals(2)){//某日类型徽章
                    if (ridingTotal>=medal.getUpToStandard()){
                        AppUserMedal appUserMedal = new AppUserMedal();
                        appUserMedal.setUserId(userId);
                        appUserMedal.setMedalId(medal.getId());
                        appUserMedal.setObtainTime(sportModeInfo.getTime());
                        bathAddMedalList.add(appUserMedal);
                    }
                }
            }
        }
        //上传统计之后的数据
        userTotalService.updateRidingTotal(userId, ridingTotal);
        //点亮勋章
        if (bathAddMedalList != null && bathAddMedalList.size() > 0) {
            medalService.bathAddUserMedal(bathAddMedalList);
        }
        //处理卡路里数据
        List<UpHeat> heats = new ArrayList<>();
        UpHeat heat = new UpHeat();
        heat.setTime(DateUtils.format(sportModeInfo.getTime(),"yyyy-MM-dd"));
        heat.setTotalCalories(sportModeInfo.getCalories());
        heats.add(heat);
        lightUpHeat(userId,heats,userTotal);
    }





    //点亮热量勋章
    public void lightUpHeat(Long userId,List<UpHeat> heats,AppUserTotal appUserTotal) {
        logger.info("点亮热量勋章 lightUpHeat : userId={},appUserTotal={} " + userId, appUserTotal.toString());
        if (heats==null&&heats.size()==0){
            return;
        }
        List<Medal> medals = medalService.getUserNotBoundMedal(userId, 4);
        //保证热量数据顺序(累计时需要保障)
        heats=heats.stream().sorted(Comparator.comparing(UpHeat::getTime)).collect(Collectors.toList());
        List<AppUserMedal> bathAddMedalList = new ArrayList<>();//此集合用于收集勋章的点亮
        if (medals!=null&&medals.size()>0){
            for (Medal medal : medals) {
                if (medal.getTimeType().equals(1)) {//单日类型徽章
                    for (UpHeat heat : heats) {
                        if (heat.getTotalCalories() >= medal.getUpToStandard()) {//总运动步数大于勋章目标值则记录
                            AppUserMedal appUserMedal = new AppUserMedal();
                            appUserMedal.setUserId(userId);
                            appUserMedal.setMedalId(medal.getId());
                            try {
                                appUserMedal.setObtainTime(DateUtils.parse(heat.getTime(), "yyyy-MM-dd"));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                            bathAddMedalList.add(appUserMedal);
                            break;
                        }
                    }
                } else if (medal.getTimeType().equals(2)) {//累计类型徽章
                    //定义累加值
                    Integer addHeatTotal = appUserTotal.getHeatTotal();
                    for (UpHeat heat : heats) {
                        addHeatTotal += heat.getTotalCalories();
                        if (addHeatTotal >= medal.getUpToStandard()) {
                            AppUserMedal appUserMedal = new AppUserMedal();
                            appUserMedal.setUserId(userId);
                            appUserMedal.setMedalId(medal.getId());
                            try {
                                appUserMedal.setObtainTime(DateUtils.parse(heat.getTime(), "yyyy-MM-dd"));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                            bathAddMedalList.add(appUserMedal);
                            break;
                        }
                    }
                }
            }
        }
        //统计数据
        int sum = heats.stream().mapToInt(UpHeat::getTotalCalories).sum();
        Integer caloriesTotal = appUserTotal.getHeatTotal() + sum;//统计数据
        //上传统计之后的数据
        userTotalService.updateCaloriesTotal(userId, caloriesTotal);
        //点亮勋章
        if (bathAddMedalList != null && bathAddMedalList.size() > 0) {
            medalService.bathAddUserMedal(bathAddMedalList);
        }

    }

    /**
     * 获取注解中对方法的描述信息 （获取注解对象）
     *
     * @param joinPoint 切点
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public static LightUpMedal getControllerAnnotation(JoinPoint joinPoint) {
        String targetName;
        String methodName = null;
        Object[] arguments = null;
        Class targetClass;
        Method[] methods = null;
        LightUpMedal logAnnotation = null;
        try {
            targetName = joinPoint.getTarget().getClass().getName();
            methodName = joinPoint.getSignature().getName();
            arguments = joinPoint.getArgs();
            targetClass = Class.forName(targetName);
            methods = targetClass.getMethods();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    logAnnotation = method.getAnnotation(LightUpMedal.class);
                    break;
                }
            }
        }
        return logAnnotation;
    }


}
